#include "include/syscall.h"
#include "include/types.h"
#include "include/segment.h"
#include "include/vesa.h"

#define MSR_STAR 0xc0000081 /* The ID of register MSR(STAR) */
#define MSR_LSTAR 0xc0000082 /* The ID of register MSR(LSTAR) */
#define MSR_SYSCALL_MASK 0xc0000084 /* The ID of register MSR(SFMASK) */

#define RF_IF 0x00000200 /* Flag register mask, used to turn off interrupt IF bit (9th bit) */

typedef unsigned long (*fn_ptr)(); // Syscall function pointer

unsigned long do_sleep(long ms); // The return value type must be consistent with the definition of fn_ptr
unsigned long do_shm(const char* name); // defined in ipc/shm.c, default `extern` here
unsigned long do_fbmap(); // defined in drivers/vesa.c
unsigned long do_get_mode_info(struct mode_info *mode_info); // defined in drivers/vesa.c

fn_ptr syscall_table[] = { do_sleep, do_shm, do_fbmap, do_get_mode_info }; // Syscall function table

void syscall_init() {
  /*
  *                          -------
  *           %EDX(high) -> | 32bit |
  *                          -------
  *           %EAX(low)  -> | 32bit |
  *                          -------
  *  `wrmsr`  %ECX(ID)   ->    MSR (STAR | LSTAR | SFMASK)
  * 
  * 
  * Set MSR(STAR) value (64bit)
  *  ------------- --------------- ----------------------------------
  * |  USER32_CS  |   KERNEL_CS   | 32bit mode syscall entry address |
  *  ------------- --------------- ----------------------------------
  *    high 16bit    middle 16bit        low 32bit (Unused)
  * 
  * Set MSR(LSTAR) value (64bit)
  *  ----------------------------------------------------------------
  * | 64bit mode syscall entry address                               |
  *  ----------------------------------------------------------------
  *                         total 64bit
  * 
  * Set MSR(SFMASK) value (64bit)
  *  ----------------------------------------------------------------
  * |         reserved            |        Flag register mask        |
  *  ----------------------------------------------------------------
  *        high 32bit(Unused)                 low 32bit
  */
  uint64_t star_val = (uint64_t)USER32_CS << 48 | (uint64_t)KERNEL_CS << 32; // Set to high 32 bits
  uint64_t syscall_entry = (uint64_t)system_call; // 64bit mode syscall entry address
  uint64_t syscall_mask = RF_IF;

  // Use instruction `wrmsr` and Register ECX(c) | EAX(a) | EDX(d) to set each MSR value in sequence
  __asm__("wrmsr": :"c"(MSR_STAR), "a"((uint32_t)star_val), "d"(star_val >> 32));
  __asm__("wrmsr": :"c"(MSR_LSTAR), "a"((uint32_t)syscall_entry), "d"(syscall_entry >> 32));
  __asm__("wrmsr": :"c"(MSR_SYSCALL_MASK), "a"((uint32_t)syscall_mask), "d"(syscall_mask >> 32));
}

